import { Injectable } from '@angular/core';
import {BehaviorSubject, combineLatest, map, Observable, Subject} from 'rxjs';
import { SelectionModel } from '@angular/cdk/collections';
import {takeUntil} from "rxjs/operators";

let uniqueId = 0;

export interface InitConfig {
  mode: 'single' | 'multi';
  labelField: string;
  valueField: string;
  selectedValue: any[];
  maxCountDisplay: number;
}

@Injectable()
export class AvtSelectDataService {

  // 用于统一的取消订阅
  destroy$ = new Subject<void>();
  // cdk 单选多选工具
  selectionMode!: SelectionModel<any>;
  // 选择模式 单选 | 多选
  mode!: 'single' | 'multi';
  // 每个select 组件的唯一 id
  private compId: number;
  // 从数据源选择的 绑定字段
  valueField: string = 'value';
  // 从数据源选择的 要显示的字段
  labelField: string = 'label';
  // max display count
  maxCountDisplay$!: BehaviorSubject<number>;
  // 选择的数据
  selectedData$!: Observable<any[]>
  // 最后显示文本
  finalDisplay$!: Observable<string[]>
  // 原始数据
  dataSource$ = new BehaviorSubject<any[]>([]);
  // 是否全选了
  isSelectedAll$!: Observable<boolean>;
  hasSelectedData$!: Observable<boolean>;
  // 搜索字符串
  filterString$ = new BehaviorSubject<string>('');
  // 搜索数据
  filterDataSource$ = combineLatest([this.dataSource$, this.filterString$]).pipe(
    takeUntil(this.destroy$),
    map(([datasource, str]) =>
      datasource.filter(d => (d[this.labelField] as string).includes(str))
    )
  );
  // 过滤后是否有数据
  isExistOption$ = this.filterDataSource$.pipe(
    map(filterData => !!filterData.length)
  )
  // 单选模式， 清除所有选择时触发，在 avt-option-panel中订阅，用来更新。
  singleClear$ = new Subject<void>();
  constructor() {
    this.compId = uniqueId++
  }

  get dataSourceLength() {
    return this.dataSource$.value.length;
  }
  get selectedData() {
    return this.selectionMode.selected;
  }
  init( {mode, labelField, valueField, selectedValue, maxCountDisplay }: InitConfig ) {
    this.mode = mode;
    this.labelField = labelField;
    this.valueField = valueField;
    this.maxCountDisplay$ = new BehaviorSubject(maxCountDisplay);
    this.selectionMode = new SelectionModel<any>(
      this.mode === 'multi',
      selectedValue
    );
    
    // 选择的数据
    this.selectedData$ = this.selectionMode.changed.pipe(
      takeUntil(this.destroy$),
      map(change => change.source.selected)
    );
    // 是否全选
    this.isSelectedAll$ = this.selectedData$.pipe(
      takeUntil(this.destroy$),
      map(d => d.length === this.dataSourceLength)
    );
    // 是否有选择数据
    this.hasSelectedData$ = this.selectedData$.pipe(
      takeUntil(this.destroy$),
      map(d => !!d.length)
    );
    // 搜索后的数据
    this.finalDisplay$ = combineLatest([
        this.selectedData$, this.maxCountDisplay$
      ]).pipe(
      takeUntil(this.destroy$),
      map(([selectedData, maxCountDisplay]) => {
          let selectedLength = selectedData.length;
          if (selectedLength <= maxCountDisplay) {
            return selectedData.map(d => d.__label)
          } else {
            return [`Selected ${selectedLength} of ${this.dataSourceLength}`]
          }
        })
      )
  }
}
